﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using MimicAPI.DWM;
using MimicAPI.Window;

namespace Mimic
{
    /// <summary>Interaction logic for ThumbControl.xaml</summary>
    [ContentProperty("Source")]
    public partial class ThumbControl : UserControl
    {
        #region Class definition
        static readonly DependencyProperty SourceProperty;
        static readonly DependencyProperty ClientAreaOnlyProperty;
        static readonly DependencyProperty AllowEnlargementProperty;
        static readonly DependencyProperty AllowReducingProperty;
        static readonly DependencyProperty ZoomProperty;
        static readonly DependencyProperty AutoZoomProperty;
        static readonly DependencyProperty WindowNameProperty;
        static readonly DependencyProperty AllowMinimizeInTrayProperty;

        static ThumbControl()
        {
            SourceProperty = DependencyProperty.Register(
                "Source", typeof(IntPtr), typeof(ThumbControl),
                new FrameworkPropertyMetadata(IntPtr.Zero, FrameworkPropertyMetadataOptions.AffectsMeasure,
                                              (obj, args) => (obj as ThumbControl).ThumbControl_Initialise((IntPtr)args.NewValue)));

            ClientAreaOnlyProperty = DependencyProperty.Register(
                "ClientAreaOnly", typeof(bool), typeof(ThumbControl),
                new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure, ThumbControl_Update));

            AllowReducingProperty = DependencyProperty.Register(
                "AllowReducing", typeof(bool), typeof(ThumbControl),
                new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure, ThumbControl_Update));

            AllowEnlargementProperty = DependencyProperty.Register(
                "AllowEnlargement", typeof(bool), typeof(ThumbControl),
                new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure, ThumbControl_Update));

            ZoomProperty = DependencyProperty.Register(
                "Zoom", typeof(double), typeof(ThumbControl),
                new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsMeasure,
                                              (obj, args) => (obj as ThumbControl).ThumbControl_UpdateZoom()));

            AutoZoomProperty = DependencyProperty.Register(
                "AutoZoom", typeof(bool), typeof(ThumbControl),
                new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure, ThumbControl_Update));

            AllowMinimizeInTrayProperty = DependencyProperty.Register(
                "AllowMinimizeInTray", typeof(bool), typeof(ThumbControl),
                new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsMeasure));

            WindowNameProperty = DependencyProperty.Register("WindowName", typeof(string), typeof(ThumbControl));

            OpacityProperty.OverrideMetadata(
                typeof(ThumbControl),
                new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.Inherits, ThumbControl_Update));

            VisibilityProperty.OverrideMetadata(
                typeof(ThumbControl),
                new FrameworkPropertyMetadata(Visibility.Visible, FrameworkPropertyMetadataOptions.Inherits, ThumbControl_Update));
        }

        static void ThumbControl_Update(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            (obj as ThumbControl).ThumbControl_Update();
        }
        #endregion

        #region Constructor et properties accessors
        public ThumbControl()
        {
            InitializeComponent();

            LayoutUpdated += ThumbControl_LayoutUpdated;
            Unloaded += ThumbControl_Unloaded;

            // TODO : Find how to associate the datacontext in the XAML
            ThumbContextMenu.DataContext = this;
        }

        public IntPtr Source
        {
            get { return (IntPtr)this.GetValue(SourceProperty); }
            set { this.SetValue(SourceProperty, value); }
        }

        public bool ClientAreaOnly
        {
            get { return (bool)this.GetValue(ClientAreaOnlyProperty); }
            set { this.SetValue(ClientAreaOnlyProperty, value); }
        }

        public bool AllowEnlargement
        {
            get { return (bool)this.GetValue(AllowEnlargementProperty); }
            set { this.SetValue(AllowEnlargementProperty, value); }
        }

        public bool AllowReducing
        {
            get { return (bool)this.GetValue(AllowReducingProperty); }
            set { this.SetValue(AllowReducingProperty, value); }
        }

        public double Zoom
        {
            get { return (double)this.GetValue(ZoomProperty); }
            set { this.SetValue(ZoomProperty, value); }
        }

        public bool AutoZoom
        {
            get { return (bool)this.GetValue(AutoZoomProperty); }
            set { this.SetValue(AutoZoomProperty, value); }
        }

        public string WindowName
        {
            get { return (string)this.GetValue(WindowNameProperty); }
            set { this.SetValue(WindowNameProperty, value); }
        }

        public bool AllowMinimizeInTray
        {
            get { return (bool)this.GetValue(AllowMinimizeInTrayProperty); }
            set { this.SetValue(AllowMinimizeInTrayProperty, value); }
        }
        #endregion

        Thumbnail thumb;

        /// <summary>Size of the thumbnail (if it exists)</summary>
        public Size? GetSourceSize() { return thumb != null ? new Size?(thumb.GetSourceSize()) : null; }

        void ThumbControl_Update()
        {
            if (thumb == null)
                return;

            // Calculate the size of the thumbnail
            // Get the real size
            Size srcSize = thumb.GetSourceSize();
            double factor = Zoom;
            // Manage the zoom
            if (AutoZoom)
                factor = Math.Min(srcSize.Width != 0 ? ThumbCtrl.ActualWidth / srcSize.Width : 0,
                                  srcSize.Height != 0 ? ThumbCtrl.ActualHeight / srcSize.Height : 0);
            if (!AllowEnlargement)
                factor = Math.Min(factor, 1);
            if (!AllowReducing)
                factor = Math.Max(factor, 1);

            // The dependency property "Zoom" does the update !
            Zoom = factor;
            // TODO : We should not repeat the "UpdateZoom" call here... To be improved...
            updateZoom(srcSize);
        }

        void ThumbControl_UpdateZoom()
        {
            if (thumb == null)
                return;
            updateWindowName();
            updateZoom(thumb.GetSourceSize());
        }

        void updateWindowName()
        {
            WindowName = string.Format("[{0:0.0} %] {1}", Zoom * 100, Source.GetWindowName());
        }

        void updateZoom(Size realSize)
        {
            // TODO : Put those modifications in different places to minimize the number of calls !
            thumb.Visible = this.Visibility == Visibility.Visible;
            thumb.SourceClientAreaOnly = this.ClientAreaOnly;
            thumb.Opacity = this.Opacity;

            // Calculate the size of the thumbnail
            Rect destRect = new Rect(0, 0, realSize.Width * Zoom, realSize.Height * Zoom);
            Rect srcRect = new Rect(0, 0, realSize.Width, realSize.Height);

            if (destRect.Width <= ThumbCtrl.ActualWidth)
                destRect.X = (ThumbCtrl.ActualWidth - destRect.Width) / 2;
            else
            {
                // We can't show all the source...
                // TODO : Check not null
                srcRect.Width *= ThumbCtrl.ActualWidth / destRect.Width;
                destRect.Width = ThumbCtrl.ActualWidth;
            }
            if (destRect.Height <= ThumbCtrl.ActualHeight)
                destRect.Y = (ThumbCtrl.ActualHeight - destRect.Height) / 2;
            else
            {
                // We can't show all the source...
                // TODO : Check not null
                srcRect.Height *= ThumbCtrl.ActualHeight / destRect.Height;
                destRect.Height = ThumbCtrl.ActualHeight;
            }
            thumb.Source = srcRect;
            thumb.Destination = destRect;

            thumb.UpdateProperties();
        }

        bool ThumbControl_Initialise(IntPtr source)
        {
            // First we unload the previous thumbnail (if it exists)
            ThumbControl_Unloaded(this, null);
            if (source == IntPtr.Zero)
                return false;

            try
            {
                thumb = new Thumbnail(this.ThumbCtrl, source);
                ThumbControl_Update();
                updateWindowName();
                return true;
            }
            catch
            {
                // We catch here errors like trying to create a thumbnail of a closed windows
                return false;
            }
        }

        void ThumbControl_Unloaded(object sender, RoutedEventArgs e)
        {
            if (thumb != null)
                thumb.Dispose();
            thumb = null;
        }

        // Main function
        void ThumbControl_LayoutUpdated(object sender, EventArgs e)
        {
            // The thumbnail does not exist and its creation failed
            if ((thumb == null) && !ThumbControl_Initialise(this.Source))
                return;

            if (thumb.IsStillAlive)
                ThumbControl_Update();
            else
                ThumbControl_Unloaded(sender, null);
        }

        /// <summary>Function used by parent to calculate the desired size</summary>
        protected override Size MeasureOverride(Size constraint)
        {
            if (thumb == null)
                return new Size(0, 0);
            Size srcSize = thumb.GetSourceSize();
            double factor = Math.Min(srcSize.Width != 0 ? constraint.Width / srcSize.Width : 0,
                                     srcSize.Height != 0 ? constraint.Height / srcSize.Height : 0);
            if (double.IsNaN(factor) || double.IsInfinity(factor))
                return new Size(srcSize.Width, srcSize.Height);
            return new Size(srcSize.Width * factor, srcSize.Height * factor);
        }

        void miEnlargement_Click(object sender, RoutedEventArgs e)
        {
            AllowEnlargement = !AllowEnlargement;
        }

        void miReducing_Click(object sender, RoutedEventArgs e)
        {
            AllowReducing = !AllowReducing;
        }

        void miAutoZoom_Click(object sender, RoutedEventArgs e)
        {
            AutoZoom = !AutoZoom;
        }

        void miMinInTray_Click(object sender, RoutedEventArgs e)
        {
            if (AllowMinimizeInTray)
                Source.MinUnminInTray(System.Drawing.Icon.FromHandle(Properties.Resources.bullet_black.GetHicon()));
        }
    }
}
